<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Document</title>
</head>

<body>
  <script>
    class Vue {
      constructor(options) {
        this.$options = options;
        this.$data = options.data;
        this.observe(this.$data, false);
      }

      //劫持数据
      observe(target, loop = true) {
        if (!target || typeof target !== 'object') {
          return target;
        }
        //处理数组
        if (Array.isArray(target)) {
          target.__proto__ = this.dealArr();
        }
        Object.keys(target).forEach((key) => {
          //监听$data里面的数据
          this.definedReactive(target, key, target[key]);
          //将$data里面的数据 绑定再vue实例属性上
          !loop && this.proxyData(key);
        });
      }

      //处理数组问题
      dealArr() {
        const oldArr = Array.prototype;
        const newArr = Object.create(oldArr);
        ['push', 'pop', 'shift', 'unshift', 'splice'].forEach((methodsName) => {
          newArr[methodsName] = function () {
            console.log('数组更新视图');
            oldArr[methodsName].call(this, ...arguments);
          };
        });
        return newArr
      }
      // 在vue根上定义属性代理data中的数据
      proxyData(key) {
        Object.defineProperty(this, key, {
          get() {
            return this.$data[key]; //触发definedReactive里面的get
          },
          set(newVal) {
            this.$data[key] = newVal; //触发definedReactive里面的set
          },
        });
      }
      definedReactive(target, key, value) {
        this.observe(value);
        Object.defineProperty(target, key, {
          get() {
            return value;
          },
          set: (newValue) => {
            //newValue  修改的数据
            this.observe(newValue); //将更改的数据也进行监听
            if (newValue !== value) {
              value = newValue;
              console.log(`$data更新了${newValue}`);
            }
          },
        });
      }
    }
    let dom = new Vue({
      data: {
        //   name: '你好',
        //   age: 18,
        //   test: {
        //     name2: 'hi',
        //   },
        //   test2: {
        //     name2: 'hi',
        //   },
        arr: [1, 2, 3],
      },
    });
    //   dom.$data.name = '更改了';
    //   dom.age = 20;
    //   dom.arr.push(4);
    // dom.$data.arr[0] = 10
    dom.$data.arr.push(4);
    //   dom.arr.push(4);
  </script>
</body>

</html>